package com.vlyman.captcha;

import com.vlyman.captcha.coder.GifEncoder;
import com.vlyman.captcha.utils.MathUtils;
import com.vlyman.captcha.utils.Streams;
import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Encoder;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;

/**
 * 类描述：
 *  Gif验证码
 * @author chenjs
 * @date 2018/12/21 12:30
 **/
@Slf4j
public class GifCaptcha extends AbstractCaptcha {

    /**
     * 方法描述：
     * 验证码输出
     *
     * @param os 输出流
     * @author chenjs
     * @date 2018/12/21 12:34
     */
    @Override
    public void out(OutputStream os) {
        try {

            GifEncoder gifEncoder = new GifEncoder();
            //生成字符
            gifEncoder.start(os);
            gifEncoder.setQuality(180);
            gifEncoder.setDelay(100);
            gifEncoder.setRepeat(0);
            BufferedImage frame;
            setCode(createCode());
            Color[] fontColorArr = createCodeRandomColor(getCode());
            for(int i=0;i<len;i++)
            {
                frame = graphicsImage(fontColorArr, getCode().toCharArray(), i);
                gifEncoder.addFrame(frame);
                frame.flush();
            }
            gifEncoder.finish();
        } catch (Exception e) {
            log.warn("创建验证码出错！", e);
        } finally {
            Streams.close(os);
        }
    }

    /**
     * 方法描述：
     * 验证码输出为base64字符串
     *
     * @param os 输出流
     * @return String base64字符串
     * @author chenjs
     * @date 2018/12/21 12:34
     */
    @Override
    public String base64Out(ByteArrayOutputStream os) {
        out(os);
        BASE64Encoder encoder = new BASE64Encoder();
        //消除生成的base64码有回车符
        return encoder.encode(os.toByteArray()).replaceAll("\r|\n", "");
    }

    /**
     * 方法描述：
     * 验证码以图片形式输出
     *
     * @param path 验证码图片输出路径
     * @author chenjs
     * @date 2018/12/22 09:12
     */
    @Override
    public void fileOutput(String path) {
        try {
            out(new FileOutputStream(path));
        } catch (FileNotFoundException e) {
            log.error("输出验证码出错！", e);
        }
    }

    /**
     * 方法描述：
     *   画随机码图
     *  @author chenjs
     *  @param fontColor 随机字体颜色
     *  @param codeArr 验证码字符组
     *  @param flag 透明度使用
     *  @return BufferedImage
     *  @date 2018/12/21 18:02
     */
    private BufferedImage graphicsImage(Color[] fontColor, char[] codeArr, int flag){
        BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
        //得图形上下文
        Graphics2D g2d = (Graphics2D)image.getGraphics();
        //利用指定颜色填充背景
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, width, height);
        AlphaComposite ac3;
        int h  = height - ((height - font.getSize()) >>1) ;
        int w = width/len;
        g2d.setFont(font);
        for(int i=0;i<len;i++)
        {
            ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i));
            g2d.setComposite(ac3);
            g2d.setColor(fontColor[i]);
            g2d.drawOval(MathUtils.getRandomInt(width),
                    MathUtils.getRandomInt(height),
                    5 + MathUtils.getRandomInt(10),
                    5 + MathUtils.getRandomInt(10));
            g2d.drawString(codeArr[i]+"", (width-(len-i)*w)+(w-font.getSize())+1, h-4);
        }
        g2d.dispose();
        return image;
    }

    /**
     * 方法描述：
     *   获取透明度,从0到1,自动计算步长
     *  @author chenjs
     *  @return float 透明度
     *  @date 2018/12/21 17:55
     */
    private float getAlpha(int i,int j){
        int num = i+j;
        float r = (float)1/len,s = (len+1) * r;
        return num > len ? (num *r - s) : num * r;
    }

    public static void main(String[] args){
        try {
            //gif格式动画验证码
            AbstractCaptcha captcha = new GifCaptcha();
            String base64Str = captcha.base64Out(new ByteArrayOutputStream());
            System.out.println(captcha.getCode());
            System.out.println(base64Str);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
